import { AssetManager, assetManager, instantiate, isValid, Layers, Node, Prefab, resources, UITransform, warn, Widget } from "cc";
import { UICallbacks, UIConfig, ViewParams } from "../Defines";
import { UIDelegateComponent } from "../UIDelegateComponent";
import { UICloseCom } from "../com/UICloseCom";

/**
 * 界面容器
 */
export class UIContainer extends Node {

    /** 界面节点集合 */
    protected ui_nodes = new Map<string, ViewParams>();
    /** 被移除的界面缓存数据 */
    protected ui_cache = new Map<string, ViewParams>();


    constructor(name: string) {
        super(name);
        this.add2DDefaultComponent();
    }

    /** 添加2D节点默认组件 */
    private add2DDefaultComponent() {
        // 2D相机渲染
        this.layer = Layers.BitMask.UI_2D;

        // 界面容器节点添加Widget组件,Widget依赖UITransform组件,会自动添加
        const widget: Widget = this.addComponent(Widget);
        widget.isAlignLeft = widget.isAlignRight = widget.isAlignTop = widget.isAlignBottom = true;
        widget.left = widget.right = widget.top = widget.bottom = 0;
        widget.alignMode = Widget.AlignMode.ON_WINDOW_RESIZE;
        widget.enabled = true;
    }

   
    /**
     * 添加一个预制件节点到层容器中，该方法将返回一个唯一`uuid`来标识该操作节点
     * @param config 界面配置
     * @param params     自定义参数
     * @param callbacks  回调函数对象，可选
     * @returns 
     */
    public add(uiId: number, config: UIConfig, params?: any, callbacks?: UICallbacks): string {
        let prefabPath = config.path;
        let uuid = this.getUuid(prefabPath);
        let viewParams = this.ui_nodes.get(uuid);

        if (viewParams && viewParams.valid) {
            warn(`路径为【${prefabPath}】的预制重复加载`);
            return "";
        }

        if (viewParams == null) {
            viewParams = new ViewParams();
            viewParams.uuid = uuid;
            viewParams.uiId = uiId;
            viewParams.prefabPath = prefabPath;
            viewParams.bundleName = config.bundle;
            this.ui_nodes.set(viewParams.uuid, viewParams);
        }

        viewParams.params = params ?? {};
        viewParams.callbacks = callbacks ?? {};
        viewParams.valid = true;

        this.load(viewParams, config.bundle);

        return uuid;
    }

    /**
    * 根据预制件路径删除，预制件如在队列中也会被删除，如果该预制件存在多个也会一起删除
    * @param prefabPath   预制路径
    * @param isDestroy    移除后是否释放
    */
    public remove(prefabPath: string, isDestroy: boolean): void {
        // 验证是否删除后台缓存界面
        if (isDestroy) this.removeCache(prefabPath);

        // 界面移出舞台
        let children = this.__nodes();
        for (let i = 0; i < children.length; i++) {
            let viewParams = children[i].viewParams;
            if (viewParams.prefabPath === prefabPath) {
                if (isDestroy) {
                    // 直接释放界面
                    this.ui_nodes.delete(viewParams.uuid);
                }
                else {
                    // 不释放界面，缓存起来待下次使用
                    this.ui_cache.set(viewParams.prefabPath, viewParams);
                }

                children[i].remove(isDestroy);
                viewParams.valid = false;
            }
        }
    }

    /** 层节点数量 */
    public size(): number {
        return this.children.length;
    }

    /**
     * 清除所有节点，队列当中的也删除
     * @param isDestroy  移除后是否释放
     */
    public clear(isDestroy: boolean): void {
        // 清除所有显示的界面
        this.ui_nodes.forEach((value: ViewParams, key: string) => {
            this.removeByUuid(value.uuid, isDestroy);
            value.valid = false;
        });
        this.ui_nodes.clear();

        // 清除缓存中的界面
        if (isDestroy) {
            this.ui_cache.forEach((value: ViewParams, prefabPath: string) => {
                this.removeCache(prefabPath);
            });
        }
    }


     /**
     * 根据唯一标识获取节点，如果节点不存在或者还在队列中，则返回null 
     * @param uuid  唯一标识
     */
    public getByUuid(uuid: string): Node {
        let children = this.__nodes();
        for (let comp of children) {
            if (comp.viewParams && comp.viewParams.uuid === uuid) {
                return comp.node;
            }
        }
        return null!;
    }

    /**
     * 根据预制件路径获取当前显示的该预制件的所有Node节点数组
     * @param prefabPath 
     */
    public get(prefabPath: string): Array<Node> {
        let arr: Array<Node> = [];
        let children = this.__nodes();
        for (let comp of children) {
            if (comp.viewParams.prefabPath === prefabPath) {
                arr.push(comp.node);
            }
        }
        return arr;
    }

    /**
     * 判断当前层是否包含 uuid或预制件路径对应的Node节点
     * @param prefabPathOrUUID 预制件路径或者UUID
     */
    public has(prefabPathOrUUID: string): boolean {
        let children = this.__nodes();
        for (let comp of children) {
            if (comp.viewParams.uuid === prefabPathOrUUID || comp.viewParams.prefabPath === prefabPathOrUUID) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取当前层包含指定正则匹配的Node节点。
     * @param prefabPathReg 匹配预制件路径的正则表达式对象
     */
    public find(prefabPathReg: RegExp): Node[] {
        let arr: Node[] = [];
        let children = this.__nodes();
        for (let comp of children) {
            if (prefabPathReg.test(comp.viewParams.prefabPath)) {
                arr.push(comp.node);
            }
        }
        return arr;
    }


    /**
     * 加载界面资源
     * @param viewParams 显示参数
     * @param bundlenName     远程资源包名，如果为空就是默认本地资源包
     */
    protected load(viewParams: ViewParams, bundlenName: string = 'resources') {
        const vp: ViewParams = this.ui_nodes.get(viewParams.uuid)!;
        if (vp && vp.node) {
            this.createNode(vp);
        }
        else {
            if (bundlenName == 'resources') {
                this.bundleLoad(resources, viewParams);
            }
            else {
                assetManager.loadBundle(bundlenName, (err, bundle) => {
                    if (err) {
                        console.log(err);
                    }
                    else {
                        this.bundleLoad(bundle, viewParams);
                    }
                })
            }
        }
    }

    /**
     * 从资源包加载界面资源
     * @param bundle 资源包
     * @param viewParams 显示参数
     */
    private bundleLoad(bundle: AssetManager.Bundle, viewParams: ViewParams) {
        console.log("UIContainer bundleLoad ->", viewParams);

        bundle.load(viewParams.prefabPath, Prefab, (err: Error | null, res: Prefab) => {
            if (err) {
                console.log(err);
            }
            else {
                res.addRef();
                let childNode: Node = instantiate(res);
                viewParams.node = childNode;

                let comp: UIDelegateComponent = childNode.addComponent(UIDelegateComponent);
                comp.viewParams = viewParams;
                

                let closeCom: UICloseCom = childNode.addComponent(UICloseCom);

                this.createNode(viewParams);
            }
        })
    }

    /**
     * 创建界面节点
     * @param viewParams  视图参数
     */
    protected createNode(viewParams: ViewParams) {
        viewParams.valid = true;

        let comp: UIDelegateComponent = viewParams.node.getComponent(UIDelegateComponent)!;
        comp.add();
        viewParams.node.parent = this;

        return viewParams.node;
    }

    /** 
     * 删除缓存的界面，当缓存界面被移除舞台时，可通过此方法删除缓存界面
     */
    private removeCache(prefabPath: string) {
        let viewParams = this.ui_cache.get(prefabPath);
        if (viewParams) {
            let childNode = viewParams.node;
            let comp = childNode.getComponent(UIDelegateComponent)!
            comp.remove(true);
            this.ui_nodes.delete(viewParams.uuid);
            this.ui_cache.delete(prefabPath);
        }
    }

    /**
    * 根据唯一标识删除节点，如果节点还在队列中也会被删除
    * @param uuid  唯一标识
    */
    protected removeByUuid(uuid: string, isDestroy: boolean): void {
        let viewParams = this.ui_nodes.get(uuid);
        if (viewParams) {
            if (isDestroy)
                this.ui_nodes.delete(viewParams.uuid);

            let childNode = viewParams.node;
            let comp = childNode.getComponent(UIDelegateComponent)!;
            comp.remove(isDestroy);
        }
    }

    /** 获取当前层所有窗口事件触发组件 */
    protected __nodes(): Array<UIDelegateComponent> {
        let result: Array<UIDelegateComponent> = [];
        let children = this.children;
        for (let i = 0; i < children.length; i++) {
            let comp = children[i].getComponent(UIDelegateComponent);
            if (comp && comp.viewParams && comp.viewParams.valid && isValid(comp)) {
                result.push(comp);
            }
        }
        return result;
    }

     /** 构造一个唯一标识UUID */
    protected getUuid(prefabPath: string): string {
        const uuid = `${this.name}_${prefabPath}`;
        return uuid.replace(/\//g, "_");
    }

}